using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;
using System.Collections;
using System.Security.Principal;
using CPGVertragsverwaltung.ErrorHandling;
using System.Diagnostics;
using System.Windows.Forms;



namespace CPGVertragsverwaltung.SecurityController
{
    class ADServices
    {

        /// <summary>
        /// Get GUID of User/Group by Accountname
        /// </summary>
        /// <param name="pSAMAccountname">Account Name</param>
        /// <returns>Hex Octet in String</returns>
        public static String getGUIDFromName(String pSAMAccountname)
        {
            String result = null;
            DirectoryEntry mEntry = new DirectoryEntry();
            DirectorySearcher mySearcher = new DirectorySearcher();
            mySearcher.SearchRoot = mEntry;
            mySearcher.Filter = "(SAMAccountName=" + pSAMAccountname + ")";

            SearchResult groupResult = mySearcher.FindOne();
            if (groupResult != null)
            {
                DirectoryEntry groupEntry = groupResult.GetDirectoryEntry();

                //Get objectGUID as bytearray
                Guid objectGuid = new Guid((byte[])groupEntry.Properties["objectGUID"].Value);
                byte[] arrbyte = objectGuid.ToByteArray();

                //Use this Format: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
                result = objectGuid.ToString("B");
            }
            return result;
        }

        /// <summary>
        /// Get SID of Object by Acccountname
        /// </summary>
        /// <param name="pSAMAccountname">Account Name</param>
        /// <returns>SID</returns>
        public static String getSIDFromName(String pSAMAccountname)
        {
            String result = null;

            SearchResult groupResult = ADServices.SearchOneADObject("(SAMAccountName=" + pSAMAccountname + ")");
            if (groupResult != null)
            {
                DirectoryEntry groupEntry = groupResult.GetDirectoryEntry();
                byte[] byteArray = (byte[])groupEntry.Properties["objectSid"][0];
                SecurityIdentifier mySID = new SecurityIdentifier(byteArray, 0);

                result = mySID.ToString();
            }
            return result;
        }

        /// <summary>
        /// Get AccountName from GUID
        /// </summary>
        /// <param name="pGUID">GUID in String of Format: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</param>
        /// <returns>Account Name</returns>
        public static String getNameFromGUID(String pGUID)
        {
            String result = null;
            DirectoryEntry mEntry = new DirectoryEntry();
            DirectorySearcher mySearcher = new DirectorySearcher();

            //Change Format to OctetString
            Guid objectGUID = new Guid(pGUID);
            String strGUID = GUID2OctectString(objectGUID);

            mySearcher.SearchRoot = mEntry;
            mySearcher.Filter = "(objectGUID=" + strGUID + ")";

            SearchResult groupResult = mySearcher.FindOne();
            if (groupResult != null)
            {
                DirectoryEntry groupEntry = groupResult.GetDirectoryEntry();
                result = groupEntry.Properties["SAMAccountName"][0].ToString();
            }
            return result;
        }

        #region private utility functions

        /// <summary>
        /// Change GUID to OctetString
        /// </summary>
        /// <param name="pGUID">GUID</param>
        /// <returns></returns>
        private static String GUID2OctectString(Guid pGUID)
        {
            String strGUID = "";
            foreach (byte b in pGUID.ToByteArray())
            {
                strGUID += @"\" + b.ToString("x1");
            }
            return strGUID;
        }

        private static String SID2OctectString(byte[] pSID)
        {
            String result = "";
            foreach (byte b in pSID)
            {
                result += @"\" + b.ToString("x1");
            }
            return result;
        }

        private static String SID2OctectString(String pSID)
        {
            String result = "";
            SecurityIdentifier SID = new SecurityIdentifier(pSID);
            byte[] arrSID = new byte[SID.BinaryLength];
            SID.GetBinaryForm(arrSID, 0);
            result = SID2OctectString(arrSID);
            return result;
        }


        private enum ADObjectType
        {
            User,
            Group,
            Else
        }

        /// <summary>
        /// Get Object-Class of the Entry
        /// </summary>
        /// <param name="pEntry">Directory Entry</param>
        /// <returns></returns>
        private static ADObjectType getObjectTypeFromEntry(DirectoryEntry pEntry)
        {
            ADObjectType result = ADObjectType.Else;
            foreach (object Property in pEntry.Properties["objectClass"])
            {
                String objectClass = Property.ToString();
                switch (objectClass)
                {
                    case "group":
                        result = ADObjectType.Group;
                        break;
                    case "user":
                        result = ADObjectType.User;
                        break;
                }
            }
            return result;
        }

        /// <summary>
        /// Get all Parents  GUID in Format of {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} from this Directory Entry
        /// </summary>
        /// <param name="pEntry">Directory Entry</param>
        /// <returns>ArrayList of GUID</returns>
        private static ArrayList getAllParentsGUIDFromPath(String pPath)
        {
            ArrayList result = new ArrayList();
            //Add itself to ArrayList
            DirectoryEntry myEntry = new DirectoryEntry(pPath);
            result.Add(myEntry.Guid.ToString("B"));

            foreach (object obj in myEntry.Properties["memberOf"])
            {
                //Use Recursiv to find parent groups
                ArrayList tempResult = getAllParentsGUIDFromPath("LDAP://" + obj.ToString());
                foreach (object tempObj in tempResult)
                {
                    result.Add(tempObj);
                }
            }

            return result;
        }

        /// <summary>
        /// Get all Children GUID in Format of {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} from this Directory Entry
        /// </summary>
        /// <param name="pEntry">Directory Entry</param>
        /// <returns>ArrayList of GUID</returns>
        private static ArrayList getAllChildrenGUIDFromPath(String pPath)
        {
            ArrayList result = new ArrayList();
            //Add itself to ArrayList
            DirectoryEntry myEntry = new DirectoryEntry(pPath);
            result.Add(myEntry.Guid.ToString("B"));

            foreach (object obj in myEntry.Properties["member"])
            {
                //Use Recursiv to find parent groups
                ArrayList tempResult = getAllChildrenGUIDFromPath("LDAP://" + obj.ToString());
                foreach (object tempObj in tempResult)
                {
                    //Check if Object is already in list
                    Boolean isInList = false;
                    foreach (String tempString in result)
                    {
                        if (tempString == tempObj.ToString())
                        {
                            isInList = true;
                        }
                    }

                    if (!isInList)
                        result.Add(tempObj);
                }
            }

            return result;
        }

        /// <summary>
        /// Get GUID from given SID
        /// </summary>
        /// <param name="pSID">SID</param>
        /// <returns>GUID or null</returns>
        public static String getGUIDFromSID(String pSID)
        {
            String result = null;

            SearchResult groupResult = ADServices.SearchOneADObject("(objectSid=" + SID2OctectString(pSID) + ")");
            if (groupResult != null)
            {
                DirectoryEntry groupEntry = groupResult.GetDirectoryEntry();
                result = groupEntry.Guid.ToString("B");
            }

            return result;
        }
        #endregion

        public static ArrayList getGroupGUIDFromGUID(Guid pGUID)
        {
            ArrayList result = new ArrayList();

            //Search for User
            DirectoryEntry mEntry = new DirectoryEntry();
            DirectorySearcher mySearcher = new DirectorySearcher();
            mySearcher.SearchRoot = mEntry;
            mySearcher.Filter = "(objectGUID=" + GUID2OctectString(pGUID) + ")";

            SearchResult objectResult = mySearcher.FindOne();
            if (objectResult != null)
            {
                DirectoryEntry objectEntry = objectResult.GetDirectoryEntry();
                String EntryPath = objectEntry.Path;
                objectEntry.Close();
                result = getAllParentsGUIDFromPath(EntryPath);
            }

            return result;
        }


        /// <summary>
        /// Check if user is in ADUserGroup
        /// </summary>
        /// <returns>true: user can use program; false: user may not use program</returns>
        public static bool isUserAuthenticated()
        {
            bool result = false;
            IdentityReferenceCollection currentGroups = WindowsIdentity.GetCurrent().Groups;

            foreach (IdentityReference obj in currentGroups)
            {
                if (obj.Value == GlobalVariables.ADUserGroupSID)
                    result = true;
            }

            return result;
        }

        /// <summary>
        /// Check if user is in ADAdminGroup
        /// </summary>
        /// <returns>true: User is Admin</returns>
        public static bool isUserAdmin()
        {
            bool result = false;
            IdentityReferenceCollection currentGroups = WindowsIdentity.GetCurrent().Groups;
            foreach (IdentityReference obj in currentGroups)
            {
                if (obj.ToString() == GlobalVariables.ADAdminGroupSID)
                    result = true;
            }

            return result;
        }


        /// <summary>
        /// Get All ChildObject with Attributes of an Group using GroupName
        /// </summary>
        /// <param name="ParentObjectName">SAMAccountName</param>
        /// <returns>ChildObjects</returns>
        public static SecurityObjectsCollection getChildObjects(String ParentSID)
        {
            SecurityObjectsCollection result = new SecurityObjectsCollection();

            SearchResult groupResult = ADServices.SearchOneADObject("(objectSid=" + SID2OctectString(ParentSID) + ")");
            if (groupResult != null)
            {
                DirectoryEntry groupEntry = groupResult.GetDirectoryEntry();
                ArrayList ChildrenGUID = getAllChildrenGUIDFromPath(groupEntry.Path);
                foreach (String childGUID in ChildrenGUID)
                {
                    StringCollection myAttributes = getAttributesFromGUID(childGUID);
                    if (myAttributes != null)
                        result.Add(myAttributes);
                }

            }
            return result;
        }


        public static SecurityObjectsCollection getDirectChildrenFromSID(String ParentSID)
        {
            SecurityObjectsCollection result = new SecurityObjectsCollection();

            DirectoryEntry mEntry = new DirectoryEntry();
            DirectorySearcher mySearcher = new DirectorySearcher();
            mySearcher.SearchRoot = mEntry;
            mySearcher.Filter = "(objectSid=" + ParentSID + ")";

            SearchResult groupResult = mySearcher.FindOne();
            if (groupResult != null)
            {
                DirectoryEntry groupEntry = groupResult.GetDirectoryEntry();
                foreach (object childLDAPPath in groupEntry.Properties["member"])
                {
                    DirectoryEntry tempEntry = new DirectoryEntry("LDAP://" + childLDAPPath.ToString());
                    StringCollection myAttributes = getAttributesFromGUID(tempEntry.Guid.ToString("B"));
                    if (myAttributes != null)
                        result.Add(myAttributes);
                }

            }

            return result;
        }

        /// <summary>
        /// Return String ArrayList of [0: SID, 1:Displayname, 2:ObjectType]
        /// </summary>
        /// <param name="pGUID"></param>
        /// <returns></returns>
        public static StringCollection getAttributesFromGUID(String pGUID)
        {
            StringCollection result = new StringCollection();
            DirectoryEntry mEntry = new DirectoryEntry();
            DirectorySearcher mySearcher = new DirectorySearcher();

            //Change Format to OctetString
            Guid objectGUID = new Guid(pGUID);
            String strGUID = GUID2OctectString(objectGUID);

            mySearcher.SearchRoot = mEntry;
            mySearcher.Filter = "(objectGUID=" + strGUID + ")";
            mySearcher.PropertiesToLoad.Add("displayName");

            SearchResult groupResult = mySearcher.FindOne();
            if (groupResult != null)
            {
                DirectoryEntry groupEntry = groupResult.GetDirectoryEntry();

                //SID
                byte[] byteArray = (byte[])groupEntry.Properties["objectSid"][0];
                SecurityIdentifier mySID = new SecurityIdentifier(byteArray, 0);
                result.Add(mySID.ToString());

                //DisplayName
                try
                {
                    result.Add(groupEntry.Properties["displayName"][0].ToString());
                }
                catch (Exception ex)
                {
                    result.Add(Convert.ToString(groupEntry.Properties["description"][0].ToString()));
                }

                //ObjectType
                result.Add(getObjectTypeFromEntry(groupEntry).ToString());
            }
            else
            {
                return null;    //not found
            }

            return result;
        }


        public static StringCollection getAttributesFromSID(String pSID)
        {
            StringCollection result = null;
            String strGUID = getGUIDFromSID(pSID);
            if (strGUID != null)
            {
                result = getAttributesFromGUID(strGUID);
            }
            return result;
        }

        /// <summary>
        /// Get String to be Displayed in StatusBar
        /// </summary>
        /// <returns></returns>
        public static String getUserSecurityString()
        {
            String result = "";

            result += "angemeldet als: ";
            result += WindowsIdentity.GetCurrent().Name;
            result += " | Gruppe:";

            SecurityObjectsCollection myCollection = ADServices.getChildObjects(GlobalVariables.ADUserGroupSID);
            //Check if User is member of a group
            foreach (StringCollection tempSecurityObject in myCollection)
            {
                if (tempSecurityObject != null)
                {

                    String SID = tempSecurityObject[0];
                    String Name = tempSecurityObject[1];
                    String ObjectType = tempSecurityObject[2];


                    if (ObjectType.ToLower() == "group")
                    {
                        Boolean isMemberOf = false;
                        foreach (SecurityIdentifier currentGroupSID in WindowsIdentity.GetCurrent().Groups)
                        {
                            if (SID == currentGroupSID.ToString())
                                isMemberOf = true;
                        }
                        if (isMemberOf)
                            result += Name + ",";
                    }
                }

            }
            return result;
        }

        private static SearchResult SearchOneADObject(String SearchString)
        {
            SearchResult result = null;

            DirectoryEntry mEntry = new DirectoryEntry();
            DirectorySearcher mySearcher = new DirectorySearcher();
            mySearcher.SearchRoot = mEntry;
            mySearcher.Filter = SearchString;
            mySearcher.ClientTimeout = TimeSpan.FromSeconds(5);

            try
            {
                result = mySearcher.FindOne();
            }
            catch (Exception ex)
            {
                ExceptionWindow myExceptionWindow = new ExceptionWindow();
                myExceptionWindow.Exception = ex;
                myExceptionWindow.ShowDialog();
                Application.Exit();
            }

            return result;
        }

    }
}
